package com.java.base.io.netty;

/**
 * <h2>Netty高性能之道</h2>
 * <h3>异步非阻塞通信</h3>
 * 在IO编程过程中，当需要同时处理多个客户端接入请求时，可以利用多线程或者IO多路复用技术进行处理。
 * IO多路复用技术通过把多个IO的阻塞复用到同一个select的阻塞上，从而使得系统在单线程的情况下可以
 * 同时处理多个客户端请求。与传统的多线程/多进程模型比，I/O多路复用的最大优势是系统开销小，系统不
 * 需要创建新的额外进程或者线程，也不需要维护这些进程和线程的运行，降低了系统的维护工作量，节省了
 * 系统资源。
 * <p>
 * JDK1.4提供了对非阻塞IO（NIO）的支持，JDK1.5_update10版本使用epoll替代了传统的select/poll，
 * 极大的提升了NIO通信的性能。
 * <p>
 * 与Socket类和ServerSocket类相对应，NIO也提供了SocketChannel和ServerSocketChannel两种
 * 不同的套接字通道实现。这两种新增的通道都支持阻塞和非阻塞两种模式。阻塞模式使用非常简单，但是性能
 * 和可靠性都不好，非阻塞模式正好相反。开发人员一般可以根据自己的需要来选择合适的模式，一般来说，低
 * 负载、低并发的应用程序可以选择同步阻塞IO以降低编程复杂度。但是对于高负载、高并发的网络应用，需要
 * 使用NIO的非阻塞模式进行开发。
 * <p>
 * Netty架构按照Reactor模式设计和实现，它的服务端通信序如Netty1
 * <p>
 * 客户端通信序列Netty2
 * <p>
 * Netty的IO线程NioEventLoop由于聚合了多路复用器Selector，可以同时并发处理成百上千个客户端Channel，
 * 由于读写操作都是非阻塞的，这就可以充分提升IO线程的运行效率，避免由于频繁IO阻塞导致的线程挂起。另外，
 * 由于Netty采用了异步通信模式，一个IO线程可以并发处理N个客户端连接和读写操作，这从根本上解决了传统同
 * 步阻塞IO一连接一线程模型，架构的性能、弹性伸缩能力和可靠性都得到了极大的提升。
 * <h2>零拷贝</h2>
 * 很多用户都听说过Netty具有“零拷贝”功能，但是具体体现在哪里又说不清楚，本小节就详细对Netty的“零拷贝”功能进行讲解。
 * <p>
 * Netty的“零拷贝”主要体现在如下三个方面：
 * <ul>
 * <li>Netty的接收和发送ByteBuffer采用DIRECT BUFFERS，使用堆外直接内存进行Socket读写，不需要进行
 * 字节缓冲区的二次拷贝。如果使用传统的堆内存（HEAP BUFFERS）进行Socket读写，JVM会将堆内存Buffer拷贝
 * 一份到直接内存中，然后才写入Socket中。相比于堆外直接内存，消息在发送过程中多了一次缓冲区的内存拷贝。</li>
 * <li>Netty提供了组合Buffer对象，可以聚合多个ByteBuffer对象，用户可以像操作一个Buffer那样方便的对
 * 组合Buffer进行操作，避免了传统通过内存拷贝的方式将几个小Buffer合并成一个大的Buffer。</li>
 * <li>Netty的文件传输采用了transferTo方法，它可以直接将文件缓冲区的数据发送到目标Channel，避免了传统
 * 通过循环write方式导致的内存拷贝问题。</li>
 * </ul>
 * <h2>内存池</h2>
 * 随着JVM虚拟机和JIT即时编译技术的发展，对象的分配和回收是个非常轻量级的工作。但是对于缓冲区Buffer，
 * 情况却稍有不同，特别是对于堆外直接内存的分配和回收，是一件耗时的操作。为了尽量重用缓冲区，Netty提供
 * 了基于内存池的缓冲区重用机制。下面我们一起看下Netty ByteBuf的实现：
 *
 * @author xuweizhi
 * @date 2019/03/19 23:53
 */
public class NettyHigherLevelKnowledge {

    /**
     * <h1>高效的Reactor线程模型</h1>
     * 常用的Reactor线程模型有三种，分别如下：
     * <ul>
     * <li>Reactor单线程模型</li>
     * <li>Reactor多线程模型</li>
     * <li>主从Reactor多线程模型</li>
     * </ul>
     * <h2>Reactor单线程模型</h2>
     * 指的是所有的IO操作都在同一个NIO线程上面完成，NIO线程的职责如下：
     * <ul>
     * <li>作为NIO服务端，接收客户端的TCP连接；</li>
     * <li>作为NIO客户端，向服务端发起TCP连接；</li>
     * <li>读取通信对端的请求或者应答消息；</li>
     * <li>向通信对端发送消息请求或者应答消息</li>
     * </ul>
     * Reactor单线程模型示意图Reactor1
     * <p>
     * 由于Reactor模式使用的是异步非阻塞IO，所有的IO操作都不会导致阻塞，理论上一个线程可以
     * 独立处理所有IO相关的操作。从架构层面看，一个NIO线程确实可以完成其承担的职责。例如，通
     * 过Acceptor接收客户端的TCP连接请求消息，链路建立成功之后，通过Dispatch将对应的ByteBuffer
     * 派发到指定的Handler上进行消息解码。用户Handler可以通过NIO线程将消息发送给客户端。对
     * 于一些小容量应用场景，可以使用单线程模型。但是对于高负载、大并发的应用却不合适，主要原因如下：
     * <ul>
     * <li>一个NIO线程同时处理成百上千的链路，性能上无法支撑，即便NIO线程的CPU负荷达到100%，也无
     * 法满足海量消息的编码、解码、读取和发送；</li>
     * <li>当NIO线程负载过重之后，处理速度将变慢，这会导致大量客户端连接超时，超时之后往往会进行重发，
     * 这更加重了NIO线程的负载，最终会导致大量消息积压和处理超时，NIO线程会成为系统的性能瓶颈；</li>
     * <li>可靠性问题：一旦NIO线程意外跑飞，或者进入死循环，会导致整个系统通信模块不可用，不能接收和
     * 处理外部消息，造成节点故障。</li>
     * </ul>
     * <h2>Reactor多线程模型</h2>
     * 为了解决这些问题，演进出了Reactor多线程模型。Rector多线程模型与单线程模型最大的区别就是有一组
     * NIO线程处理IO操作，它的原理图Reactor2
     * <p>
     * Reactor多线程模型的特点:
     *
     * <ul>
     * <li>有专门一个NIO线程-Acceptor线程用于监听服务端，接收客户端的TCP连接请求；</li>
     * <li>网络IO操作-读、写等由一个NIO线程池负责，线程池可以采用标准的JDK线程池实现，它包含一个任
     * 务队列和N个可用的线程，由这些NIO线程负责消息的读取、解码、编码和发送；</li>
     * <li>1个NIO线程可以同时处理N条链路，但是1个链路只对应1个NIO线程，防止发生并发操作问题。</li>
     * </ul>
     * 在绝大多数场景下，Reactor多线程模型都可以满足性能需求；但是，在极特殊应用场景中，一个NIO线程负
     * 责监听和处理所有的客户端连接可能会存在性能问题。例如百万客户端并发连接，或者服务端需要对客户端的
     * 握手消息进行安全认证，认证本身非常损耗性能。在这类场景下，单独一个Acceptor线程可能会存在性能不
     * 足问题。
     * <h2>主从Reactor多线程模型</h2>
     * 为了解决性能问题，产生了第三种Reactor线程模型-主从Reactor多线程模型。
     * 它的线程模型如下图Reactor3
     * 主从Reactor线程模型的特点是：
     * <ul>
     * <li>服务端用于接收客户端连接的不再是个1个单独的NIO线程，而是一个独立的NIO线程池。</li>
     * <li>Acceptor接收到客户端TCP连接请求处理完成后（可能包含接入认证等），将新创建的SocketChannel
     * 注册到IO线程池（sub reactor线程池）的某个IO线程上，由它负责SocketChannel的读写和编解码工作。</li>
     * <li>Acceptor线程池仅仅只用于客户端的登陆、握手和安全认证，一旦链路建立成功，就将链路注册到后端
     * subReactor线程池的IO线程上，由IO线程负责后续的IO操作。</li>
     * </ul>
     * 利用主从NIO线程模型，可以解决1个服务端监听线程无法有效处理所有客户端连接的性能不足问题。因此，在
     * Netty的官方demo中，推荐使用该线程模型。事实上，Netty的线程模型并非固定不变，通过在启动辅助类中
     * 创建不同的EventLoopGroup实例并通过适当的参数配置，就可以支持上述三种Reactor线程模型。正是因为
     * Netty 对Reactor线程模型的支持提供了灵活的定制能力，所以可以满足不同业务场景的性能诉求
     * <h2>无锁化的串行设计理念</h2>
     * 在大多数场景下，并行多线程处理可以提升系统的并发性能。但是，如果对于共享资源的并发访问处理不当，会
     * 带来严重的锁竞争，这最终会导致性能的下降。为了尽可能的避免锁竞争带来的性能损耗，可以通过串行化设计，
     * 即消息的处理尽可能在同一个线程内完成，期间不进行线程切换，这样就避免了多线程竞争和同步锁。
     * <p>
     * 为了尽可能提升性能，Netty采用了串行无锁化设计，在IO线程内部进行串行操作，避免多线程竞争导致的性能
     * 下降。表面上看，串行化设计似乎CPU利用率不高，并发程度不够。但是，通过调整NIO线程池的线程参数，可以
     * 同时启动多个串行化的线程并行运行，这种局部无锁化的串行线程设计相比一个队列-多个工作线程模型性能更优。
     * Netty的串行化设计工作原理图Netty。
     * <p>
     * Netty的NioEventLoop读取到消息之后，直接调用ChannelPipeline的fireChannelRead(Object msg)，
     * 只要用户不主动切换线程，一直会由NioEventLoop调用到用户的Handler，期间不进行线程切换，这种串行化处
     * 理方式避免了多线程操作导致的锁的竞争，从性能角度看是最优的。
     * <h2>高效的并发编程</h2>
     * Netty的高效并发编程主要体现在如下几点：
     * <ul>
     * <li>volatile的大量、正确使用;</li>
     * <li>CAS和原子类的广泛使用；</li>
     * <li>线程安全容器的使用；/li>
     * <li>通过读写锁提升并发性能。</li>
     * </ul>
     *
     * <h2>高性能的序列化框架</h2>
     * <p>
     * 影响序列化性能的关键因素总结如下：
     * <ul>
     * <li>序列化后的码流大小（网络带宽的占用）；</li>
     * <li>序列化&反序列化的性能（CPU资源占用）；</li>
     * <li>是否支持跨语言（异构系统的对接和开发语言切换）。</li>
     * </ul>
     * <p>
     * Netty默认提供了对Google Protobuf的支持，通过扩展Netty的编解码接口，用户可以实现其它的高性能序列化框
     * 架，例如Thrift的压缩二进制编解码框架。
     * <p>
     * 下面我们一起看下不同序列化&反序列化框架序列化后的字节数组对比：
     * <p>
     * 从上图可以看出，Protobuf序列化后的码流只有Java序列化的1/4左右。正是由于Java原生序列化性能表现太差，才
     * 催生出了各种高性能的开源序列化技术和框架（性能差只是其中的一个原因，还有跨语言、IDL定义等其它因素）。了解
     * 更详细的测试结果
     *
     * <h2>灵活的TCP参数配置能力</h2>
     * <p>
     * 合理设置TCP参数在某些场景下对于性能的提升可以起到显著的效果，例如SO_RCVBUF和SO_SNDBUF。如果设置不当，
     * 对性能的影响是非常大的。下面我们总结下对性能影响比较大的几个配置项：
     *
     * <ul>
     * <li>SO_RCVBUF和SO_SNDBUF：通常建议值为128K或者256K；</li>
     * <li>SO_TCPNODELAY：NAGLE算法通过将缓冲区内的小封包自动相连，组成较大的封包，阻止大量小封包的发送
     * 阻塞网络，从而提高网络应用效率。但是对于时延敏感的应用场景需要关闭该优化算法；</li>
     * <li>软中断：如果Linux内核版本支持RPS（2.6.35以上版本），开启RPS后可以实现软中断，提升网络吞吐量。
     * RPS根据数据包的源地址，目的地址以及目的和源端口，计算出一个hash值，然后根据这个hash值来选择软中断运
     * 行的cpu，从上层来看，也就是说将每个连接和cpu绑定，并通过这个hash值，来均衡软中断在多个cpu上，提升网
     * 络并行处理性能。</li>
     * </ul>
     *
     * Netty在启动辅助类中可以灵活的配置TCP参数，满足不同的用户场景。相关配置接口定义Netty3：
     */
    public static void main(String[] args) {

    }
}
